﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
using System.Linq;
using System.Text;

namespace Equation.Enterprise.Framework.Domain.Data
{
    public class UnitOfWork : IUnitOfWork
    {
        private DbTransaction transaction;
        private DbContext dbContext;

        public UnitOfWork(DbContext dbContext)
        {
            this.dbContext = dbContext;
        }

        public bool IsInTransaction
        {
            get { return transaction != null; }
        }

        public void SaveChanges()
        {
            SaveChanges(SaveOptions.None);
        }

        public void SaveChanges(System.Data.Objects.SaveOptions saveOptions)
        {
            if (IsInTransaction)
                throw new ApplicationException("A transaction is running. Call BeginTransaction instead.");

            ((IObjectContextAdapter)dbContext).ObjectContext.SaveChanges(saveOptions);
        }

        public void BeginTransaction()
        {
            BeginTransaction(IsolationLevel.ReadCommitted);
        }

        public void BeginTransaction(System.Data.IsolationLevel isolationLevel)
        {
            if (transaction!=null)
                throw new ApplicationException("Cannot begin transaction while an existing transaction is running." +
                                                "Please commit or rollback the existing transaction.");

            OpenConnection();
            transaction = ((IObjectContextAdapter) dbContext).ObjectContext.Connection.BeginTransaction(isolationLevel);

        }

        private void OpenConnection()
        {
            if (((IObjectContextAdapter)dbContext).ObjectContext.Connection.State != ConnectionState.Open)
            {
                ((IObjectContextAdapter)dbContext).ObjectContext.Connection.Open();
            }
        }

        public void RollBackTransaction()
        {
            transaction.Rollback();
        }

        public void CommitTransaction()
        {
            if (transaction==null)
                throw new ApplicationException("Cannot commit transaction. No existing transaction.");

            try
            {
                ((IObjectContextAdapter) dbContext).ObjectContext.SaveChanges();
                transaction.Commit();
            }
            catch
            {
                transaction.Rollback();
                throw;
            }
            finally
            {
                ReleaseCurrentTransaction();
            }
        }

        private void ReleaseCurrentTransaction()
        {
            if (transaction != null)
            {
                transaction.Dispose();
                transaction = null;
            }
        }

#region Implementation of IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes off the managed and unmanaged resources used.
        /// </summary>
        /// <param name="disposing"></param>
        private void Dispose(bool disposing)
        {
            if (!disposing)
                return;

            if (disposed)
                return;

            disposed = true;
        }

        private bool disposed;

#endregion
    }
}
